package r3.rpc.server;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import r3.rpc.InvokePolicy;
import r3.rpc.RpcRequest;
import r3.rpc.RpcResponse;

import java.lang.reflect.Method;

/**
 * NettyRpcHandler
 *
 * @author zhoufn
 * @create 2017-12-25 11:37
 **/

@Slf4j
public class NettyRpcHandler extends SimpleChannelInboundHandler<RpcRequest> {

    /**
     * 反射策略
     */
    private Class<? extends InvokePolicy> invokePolicy;

    public NettyRpcHandler(Class<? extends InvokePolicy> invokePolicy){
        this.invokePolicy = invokePolicy;
    }

    /**
     * <strong>Please keep in mind that this method will be renamed to
     * {@code messageReceived(ChannelHandlerContext, I)} in 5.0.</strong>
     * <p>
     *
     * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *            belongs to
     * @param msg the message to handle
     * @throws Exception is thrown if an error occurred
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcRequest msg) throws Exception {
        log.debug("开始处理请求{}", msg.getRpcId());
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRpcId(msg.getRpcId());

        try {
            Object value = this.invokeMethod(msg);
            rpcResponse.setValue(value);
        } catch (Exception e) {
            log.error("请求处理({})过程中出错", msg.getRpcId(), e);
            rpcResponse.setException(e);
        }

        ctx.writeAndFlush(rpcResponse).addListener((ChannelFuture future) -> {
            future.channel().close();
            log.debug("请求处理完毕：{}", msg.getRpcId());
        });
    }

    /**
     * 反射
     *
     * @param request
     * @return
     * @throws Exception
     */
    private Object invokeMethod(RpcRequest request) throws Exception {
        Method invokeMethod = this.invokePolicy.getMethod("invoke", RpcRequest.class);
        Object instance = this.invokePolicy.newInstance();
        return invokeMethod.invoke(instance,request);
    }
}
